package com.librariy.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PointF;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
import android.widget.Scroller;
/**
 * 类似android.support.v4.view.ViewPager，水平按页循环滚动控件
 * 
 * @author luchyu
 */
public class LoopViewPager extends LinearLayout {
    private static final String TAG = LoopViewPager.class.getSimpleName();
    private OnPageChangeListener mOnPageChangeListener;
    private Boolean mPressed = false;
    private PointF mPoint = new PointF();
    private Scroller mScroller;
    private int mCurrent = 0;

    public LoopViewPager(Context context) {
        super(context);
        this.initialise();
    }

    public LoopViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.initialise();
    }

    public void initialise() {
        this.mScroller = new Scroller(getContext(), new DecelerateInterpolator(0.5f));
        super.setOrientation(LinearLayout.HORIZONTAL);
    }

    public void setOnPageChangeListener(OnPageChangeListener mOnPageChangeListener) {
        this.mOnPageChangeListener = mOnPageChangeListener;
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            super.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            super.postInvalidate();
        }
        if (mScroller.getCurrX() == mScroller.getFinalX()) {
            dealWithCycledPage();
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        updateUI(false);
    }

    public void showNext() {
        showPage(mCurrent + 1);
    }

    public void showPrevious() {
        showPage(mCurrent - 1);
    }

    public void showPage(int page) {
        if (!this.isShown()) {
            return;
        }
        int mChildCount = super.getChildCount();
        this.mCurrent = page;
        mCurrent = (mCurrent > mChildCount - 1) ? 0 : mCurrent;
        mCurrent = (mCurrent < 0) ? mChildCount - 1 : mCurrent;
        updateUI(true);
    }

    private void dealWithCycledPage() {
        int mChildCount = super.getChildCount();
        if (mChildCount < 3)
            return;
        for (int i = 0; i < super.getChildCount(); i++) {
            View iv = super.getChildAt(i);
            if (iv.getId() != mCurrent) {
                continue;
            }
            if (i == 0) {
                View v1 = super.getChildAt(mChildCount - 1);
                super.removeView(v1);
                super.addView(v1, 0, new LayoutParams(getMeasuredWidth(), LayoutParams.MATCH_PARENT));
            } else if (i == mChildCount - 1) {
                View v1 = super.getChildAt(0);
                super.removeView(v1);
                super.addView(v1, new LayoutParams(getMeasuredWidth(), LayoutParams.MATCH_PARENT));
            }
            break;
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // Log.d(TAG, "dispatchTouchEvent, ev=" + ev.toString());
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                stop();
                smoothScrollTo(mScroller.getCurrX(), mScroller.getCurrY(), false);
                mPoint.set(ev.getX(), ev.getY());
                mPressed = true;
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mPressed = false;
                mPoint.set(ev.getX(), ev.getY());
                decideToPageBoundary();
                start();
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_MOVE) {
            int distanceX = (int) ((mPoint.x - event.getX()) * 2);
            mPoint.set(event.getX(), event.getY());
            traceMoveDistanceX(distanceX);
            if (Math.abs(distanceX) > 2) {
                return true;
            }
        }
        return super.onInterceptTouchEvent(event);
    }

    @Override
    @SuppressLint("ClickableViewAccessibility")
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        if (event.getAction() == MotionEvent.ACTION_MOVE) {
            int distanceX = (int) ((mPoint.x - event.getX()) * 2);
            mPoint.set(event.getX(), event.getY());
            traceMoveDistanceX(distanceX);
        }
        return true;
    }

    public void traceMoveDistanceX(int distanceX) {
        View mView = getCurrentView();
        if (mView == null)
            return;
        int targetX = mScroller.getFinalX() + distanceX;
        if (distanceX > 0 && targetX > mView.getLeft() + super.getMeasuredWidth()) {
            targetX = mView.getLeft() + super.getMeasuredWidth();
        } else if (distanceX < 0 && targetX < mView.getLeft() - super.getMeasuredWidth()) {
            targetX = mView.getLeft() - super.getMeasuredWidth();
        }
        smoothScrollTo(targetX, mScroller.getFinalY(), false);
    }

    private void decideToPageBoundary() {
        int mChildCount = super.getChildCount();
        if (mChildCount == 0) {
            return;
        }
        int viewIndex = 0;
        for (int i = 0; i < mChildCount; i++) {
            View ivImg = super.getChildAt(i);
            int offsetLeft = mScroller.getFinalX() - ivImg.getLeft();
            if (Math.abs(offsetLeft) < getMeasuredWidth() / 2) {
                viewIndex = i;
                break;
            } else if (i == 0 && Math.abs(offsetLeft) < getMeasuredWidth()) {
                viewIndex = mChildCount - 1;
            } else if (i == mChildCount - 1 && Math.abs(offsetLeft) < getMeasuredWidth()) {
                viewIndex = 0;
            }
        }
        View v = super.getChildAt(viewIndex);
        showPage(v.getId());
    }

    private View getCurrentView() {
        if (super.getChildCount() < 1)
            return new View(getContext());
        for (int i = 0; i < super.getChildCount(); i++) {
            View iv = super.getChildAt(i);
            if (iv.getId() == mCurrent) {
                return iv;
            }
        }
        View mView = super.getChildAt(0);
        mCurrent = mView.getId();
        return mView;
    }

    private synchronized void updateUI(boolean smoothed) {
        if (mPressed)
            return;
        View mView = getCurrentView();
        if (mView == null)
            return;
        smoothScrollTo(mView.getLeft(), mView.getTop(), smoothed);
        if (mOnPageChangeListener != null) {
            mOnPageChangeListener.onPageChanged(mView, mView.getId(), super.getChildCount());
        }
    }

    public void smoothScrollTo(int x, int y, boolean isSmooth) {
        if (isSmooth) {
            float duration = (400 * 1.0f / super.getMeasuredWidth()) * Math.abs(x - mScroller.getCurrX());
            mScroller.startScroll(mScroller.getCurrX(), mScroller.getCurrY(), x - mScroller.getCurrX(), y - mScroller.getCurrY(), (int) duration);
        } else {
            mScroller.setFinalX(x);
            mScroller.setFinalY(y);
        }
        super.postInvalidate();
    }

    public void addPage(View pageView) {
        int mChildCount = super.getChildCount();
        for (int i = 0; i < mChildCount; i++) {
            super.getChildAt(i).setId(i);
        }
        pageView.setId(mChildCount);
        super.addView(pageView);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
            int width = MeasureSpec.getSize(widthMeasureSpec);
            for (int i = 0; i < super.getChildCount(); i++) {
                View v = super.getChildAt(i);
                ViewGroup.LayoutParams lp = v.getLayoutParams();
                if (lp instanceof LayoutParams) {
                    lp.width = width;
                    lp.height = LayoutParams.MATCH_PARENT;
                } else {
                    v.setLayoutParams(new LayoutParams(width, LayoutParams.MATCH_PARENT));
                }
            }
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    public void start() {
        mHander.sendEmptyMessageDelayed(0, 3000);
    }

    public void stop() {
        mHander.removeMessages(0);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        this.stop();
    }

    private Handler mHander = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            showNext();
            mHander.sendEmptyMessageDelayed(0, 3000);
        }

        @Override
        public synchronized boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            super.removeMessages(msg.what);
            return super.sendMessageAtTime(msg, uptimeMillis);
        }
    };

    public static interface OnPageChangeListener {
        public void onPageChanged(View targetPage, int currPage, int totalPage);
    }
}
